// ==++==
//
//   
//    Copyright (c) 2006 Microsoft Corporation.  All rights reserved.
//   
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//   
//    You must not remove this notice, or any other, from this software.
//   
//
// ==--==

#ifndef _EX_H_
#define _EX_H_

#include "crtwrap.h"
#include "winwrap.h"
#include "sstring.h"
#include "corerror.h"
#include "stresslog.h"
#include "genericstackprobe.h"
#include "staticcontract.h"
#include "entrypoints.h"

#if !defined(_DEBUG_IMPL) && defined(_DEBUG) && !defined(DACCESS_COMPILE)
#define _DEBUG_IMPL 1
#endif



//-------------------------------------------------------------------------------------------
// This routine will generate the most descriptive possible error message for an hresult.
// It will generate at minimum the hex value. It will also try to generate the symbolic name
// (E_POINTER) and the friendly description (from the message tables.)
//
// bNoGeekStuff suppresses hex HR codes. Use this sparingly as most error strings generated by the
// CLR are aimed at developers, not end-users.
//-------------------------------------------------------------------------------------------
void GetHRMsg(HRESULT hresult, SString &result, BOOL bNoGeekStuff = FALSE);


//-------------------------------------------------------------------------------------------
// Similar to GetHRMsg but phrased for top-level exception message.
//-------------------------------------------------------------------------------------------
void GenerateTopLevelHRExceptionMessage(HRESULT hresult, SString &result);


// ---------------------------------------------------------------------------
//   Standard exception hierarchy & infrastructure for library code & EE
// ---------------------------------------------------------------------------

// ---------------------------------------------------------------------------
// Exception class.  Abstract root exception of our hierarchy.
// ---------------------------------------------------------------------------

class Exception;
class SEHException;


class Exception
{
    friend bool DebugIsEECxxExceptionPointer(void* pv);

 private:
    static const int c_type = 0x524f4f54;   // 'ROOT'
    static Exception * g_OOMException;
    static Exception * g_SOException;

 protected:
    Exception           *m_innerException;

 public:
    Exception() {LEAF_CONTRACT; m_innerException = NULL;}
    virtual ~Exception() {LEAF_CONTRACT; if (m_innerException != NULL) Exception::Delete(m_innerException); }
    virtual BOOL IsDomainBound() {return m_innerException!=NULL && m_innerException->IsDomainBound();} ;
    virtual HRESULT GetHR() = 0;
    virtual void GetMessage(SString &s);
    virtual IErrorInfo *GetErrorInfo() { LEAF_CONTRACT; return NULL; }

    // Dynamic type query for catchers
    static int GetType() { LEAF_CONTRACT; return c_type; }
    virtual int GetInstanceType() = 0;
    virtual BOOL IsType(int type) {LEAF_CONTRACT;  return type == c_type; }

    // Will create a new instance of the Exception.  Note that this will
    // be free of app domain or thread affinity.  Not every type of exception
    // can be cloned with full fidelity.
    virtual Exception *Clone();

    // DomainBoundClone is a specialized form of cloning which is guaranteed
    // to provide full fidelity.  However, the result is bound to the current
    // app domain and should not be leaked.
    Exception *DomainBoundClone();

    class HandlerState
    {
    public:
        HandlerState();

        void CleanupTry();
        void SetupCatch(bool fCaughtInternalCxxException);
        void SucceedCatch(bool fCaughtInternalCxxException);
    };

    // Is this exception type considered "uncatchable"?
    BOOL IsTerminal();

    // Is this exception type considered "transient" (would a retry possibly succeed)?
    BOOL IsTransient();
    static BOOL IsTransient(HRESULT hr);

    // Get an HRESULT's source representation, if known
    static LPCSTR GetHRSymbolicName(HRESULT hr);

    static Exception* GetOOMException();

    static Exception* GetSOException();

    // Preallocated exceptions:  If there is a preallocated instance of some
    //  subclass of Exception, override this function and return a correct
    //  value.  The default implementation returns constant FALSE
    virtual BOOL IsPreallocatedException(); 
    BOOL IsPreallocatedOOMException();
    BOOL IsPreallocatedSOException();

    static void Delete(Exception* pvMemory);

protected:

    // This virtual method must be implemented by any non abstract Exception
    // derived class. It must allocate a NEW exception of the identical type and
    // copy all the relevant fields from the current exception to the new one.
    // It is NOT responsible however for copying the inner exception. This 
    // will be handled by the base Exception class.
    virtual Exception *CloneHelper();   

    // This virtual method must be implemented by Exception subclasses whose
    // DomainBoundClone behavior is different than their normal clone behavior. 
    // It must allocate a NEW exception of the identical type and
    // copy all the relevant fields from the current exception to the new one.
    // It is NOT responsible however for copying the inner exception. This 
    // will be handled by the base Exception class.    
    virtual Exception *DomainBoundCloneHelper() { return CloneHelper(); }
};

NEW_HOLDER_TEMPLATE1(ExceptionHolderTemplate, typename TYPE, Wrapper,
                     TYPE*, DoNothing<TYPE*>, Exception::Delete, NULL);
typedef ExceptionHolderTemplate<Exception> ExceptionHolder;

// ---------------------------------------------------------------------------
// HRException class.  Implements exception API for exceptions generated from HRESULTs
// ---------------------------------------------------------------------------

class HRException : public Exception
{
    friend bool DebugIsEECxxExceptionPointer(void* pv);

 protected:
    HRESULT             m_hr;

 public:
    HRException();
    HRException(HRESULT hr);

    static const int c_type = 0x48522020;   // 'HR  '

    // Dynamic type query for catchers
    static int GetType() {LEAF_CONTRACT;  return c_type; }
    virtual int GetInstanceType() { LEAF_CONTRACT; return c_type; }
    virtual BOOL IsType(int type) { WRAPPER_CONTRACT; return type == c_type || Exception::IsType(type);  }
    // Virtual overrides
    HRESULT GetHR();

 protected:    
    virtual Exception *CloneHelper()
    {
        WRAPPER_CONTRACT;
        return new HRException(m_hr);
    }    
};

// ---------------------------------------------------------------------------
// HRMessageException class.  Implements exception API for exceptions
// generated from HRESULTs, and includes in info message.
// ---------------------------------------------------------------------------

class HRMsgException : public HRException
{
    friend bool DebugIsEECxxExceptionPointer(void* pv);

 protected:
    SString             m_msg;

 public:
    HRMsgException();
    HRMsgException(HRESULT hr, SString &msg);

    // Virtual overrides
    void GetMessage(SString &s);

 protected:    
    virtual Exception *CloneHelper()
    {
        WRAPPER_CONTRACT;
        return new HRMsgException(m_hr, m_msg);
    }    
};

// ---------------------------------------------------------------------------
// COMException class.  Implements exception API for standard COM-based error info
// ---------------------------------------------------------------------------

class COMException : public HRException
{
    friend bool DebugIsEECxxExceptionPointer(void* pv);

 private:
    IErrorInfo          *m_pErrorInfo;

 public:
    COMException();
    COMException(HRESULT hr) ;
    COMException(HRESULT hr, IErrorInfo *pErrorInfo);
    ~COMException();

    // Virtual overrides
    IErrorInfo *GetErrorInfo();
    void GetMessage(SString &result);

 protected:    
    virtual Exception *CloneHelper()
    {
        WRAPPER_CONTRACT;
        return new COMException(m_hr, m_pErrorInfo);
    }    
};

// ---------------------------------------------------------------------------
// SEHException class.  Implements exception API for SEH exception info
// ---------------------------------------------------------------------------

class SEHException : public Exception
{
    friend bool DebugIsEECxxExceptionPointer(void* pv);

 public:
    EXCEPTION_RECORD        m_exception;

    SEHException();
    SEHException(EXCEPTION_RECORD *pRecord, CONTEXT *pContext = NULL);

    static const int c_type = 0x53454820;   // 'SEH '

    // Dynamic type query for catchers
    static int GetType() {LEAF_CONTRACT;  return c_type; }
    virtual int GetInstanceType() { LEAF_CONTRACT; return c_type; }
    virtual BOOL IsType(int type) { WRAPPER_CONTRACT; return type == c_type || Exception::IsType(type);  }

    // Virtual overrides
    HRESULT GetHR();
    IErrorInfo *GetErrorInfo();
    void GetMessage(SString &result);

 protected:    
    virtual Exception *CloneHelper()
    {
        WRAPPER_CONTRACT;
        return new SEHException(&m_exception);
    }    
};

// ---------------------------------------------------------------------------
// DelegatingException class.  Implements exception API for "foreign" exceptions.
// ---------------------------------------------------------------------------

class DelegatingException : public Exception
{
    Exception *m_delegatedException;
    Exception* GetDelegate();

    enum {DELEGATE_NOT_YET_SET = -1};
    bool IsDelegateSet() {LEAF_CONTRACT; return m_delegatedException != (Exception*)DELEGATE_NOT_YET_SET; }
    bool IsDelegateValid() {LEAF_CONTRACT; return IsDelegateSet() && m_delegatedException != NULL; }

 public:

    DelegatingException();
    ~DelegatingException();

    static const int c_type = 0x44454C20;   // 'DEL '

    // Dynamic type query for catchers
    static int GetType() {LEAF_CONTRACT; return c_type; }
    virtual int GetInstanceType() { LEAF_CONTRACT; return c_type; }
    virtual BOOL IsType(int type) { WRAPPER_CONTRACT; return type == c_type || Exception::IsType(type);  }

    // Virtual overrides
    virtual BOOL IsDomainBound() {return Exception::IsDomainBound() ||(m_delegatedException!=NULL && m_delegatedException->IsDomainBound());} ;    
    HRESULT GetHR();
    IErrorInfo *GetErrorInfo();
    void GetMessage(SString &result);
    virtual Exception *Clone();

 protected:    
    virtual Exception *CloneHelper()
    {
        WRAPPER_CONTRACT;
        return new DelegatingException();
    }    
};


//------------------------------------------------------------------------------
// class StackOverflowException
//
//   While there can be any number of instances of this class, there is one
//    special instance, the pre-allocated SO exception.  Storage for that 
//    instance is allocated in the image, so we can always obtain it, even
//    in low memory or true stack-overflow situations.
//   Note that, in fact, there is only one instance.
//------------------------------------------------------------------------------
class StackOverflowException : public Exception
{  
 private:
    static const int c_type = 0x534F2020;   // 'SO  '
    BOOL    bIsPreallocated;

 public:
     StackOverflowException();
     StackOverflowException(BOOL b);
    
     // Dynamic type query for catchers
    static int GetType();
    virtual int GetInstanceType();
    BOOL IsType(int type);
    HRESULT GetHR();
    void GetMessage(SString &result);
    virtual Exception *Clone();
    virtual BOOL IsPreallocatedException();
};


template <typename STATETYPE>
class CAutoTryCleanup
{
public:
    CAutoTryCleanup(STATETYPE& refState) :
        m_refState(refState)
    {
        LEAF_CONTRACT;

    }

    ~CAutoTryCleanup()
    {
        WRAPPER_CONTRACT;
        m_refState.CleanupTry();

    }

protected:
    STATETYPE& m_refState;

};


//
//
//


//
//
//
//




#define RethrowTerminalExceptions                                       \
    if (GET_EXCEPTION()->IsTerminal())                                  \
    {                                                                   \
        EX_RETHROW;                                                     \
    }                                                                   \

#define RethrowTransientExceptions                                      \
    if (GET_EXCEPTION()->IsTransient())                                 \
    {                                                                   \
        EX_RETHROW;                                                     \
    }                                                                   \

#define RethrowSOExceptions												\
    if (GET_EXCEPTION()->IsType(StackOverflowException::GetType())) 	\
    {                                                                   \
        EX_RETHROW;                                                     \
    }                                                                   \
	

#define SwallowAllExceptions ;


#ifdef _DEBUG

void ExThrowTrap(const char *fcn, const char *file, int line, const char *szType, HRESULT hr, const char *args);

#define EX_THROW_DEBUG_TRAP(fcn, file, line, szType, hr, args) ExThrowTrap(fcn, file, line, szType, hr, args)

#else

#define EX_THROW_DEBUG_TRAP(fcn, file, line, szType, hr, args)

#endif

#define HANDLE_SO_TOLERANCE_FOR_THROW

#define EX_THROW(_type, _args)                                                          \
    {                                                                                   \
        FAULT_NOT_FATAL();                                                              \
                                                                                        \
        _type * ___pExForExThrow =  new _type _args ;                                   \
                /* don't embed file names in retail to save space and avoid IP */       \
                /* a findstr /n will allow you to locate it in a pinch */               \
        STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW Type = 0x%x HR = 0x%x, "               \
                    INDEBUG(__FILE__) " line %d\n", _type::GetType(),                   \
                    ___pExForExThrow->GetHR(), __LINE__);                               \
        EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, #_type, ___pExForExThrow->GetHR(), #_args);          \
        HANDLE_SO_TOLERANCE_FOR_THROW;                                                  \
        PAL_CPP_THROW(_type *, ___pExForExThrow);                                       \
    }

//--------------------------------------------------------------------------------
// Clones an exception into the current domain. Also handles special cases for
// OOM and other stuff. Making this a function so we don't inline all this logic
// every place we call EX_THROW_WITH_INNER.
//--------------------------------------------------------------------------------
Exception *ExThrowWithInnerHelper(Exception *inner);

// This macro will set the m_innerException into the newly created exception
// The passed in _type has to be derived from CLRException. You cannot put OOM
// as the inner exception. If we are throwing in OOM case, allocate more memory (this macro will clone)
// does not make any sense. 
// 
#define EX_THROW_WITH_INNER(_type, _args, _inner)                                       \
    {                                                                                   \
        FAULT_NOT_FATAL();                                                              \
        Exception *_inner2 = ExThrowWithInnerHelper(_inner);                            \
        _type *___pExForExThrow =  new _type _args ;                                    \
        ___pExForExThrow->m_innerException = _inner2;                                   \
        STRESS_LOG3(LF_EH, LL_INFO100, "EX_THROW_WITH_INNER Type = 0x%x HR = 0x%x, "    \
                    INDEBUG(__FILE__) " line %d\n", _type::GetType(),                   \
                    ___pExForExThrow->GetHR(), __LINE__);                               \
        EX_THROW_DEBUG_TRAP(__FUNCTION__, __FILE__, __LINE__, #_type, ___pExForExThrow->GetHR(), #_args);          \
        HANDLE_SO_TOLERANCE_FOR_THROW;                                                  \
        PAL_CPP_THROW(_type *, ___pExForExThrow);                                       \
    }

//#define IsCLRException(ex) ((ex !=NULL) && ex->IsType(CLRException::GetType())

#define EX_TRY   EX_TRY_CUSTOM(Exception::HandlerState, DelegatingException /* was SEHException*/)

#ifndef INCONTRACT
#define INCONTRACT(x)
#endif

#define EX_TRY_CUSTOM(STATETYPE, DEFAULT_EXCEPTION_TYPE)                                \
    {                                                                                   \
        bool                    __fCaughtSO = false;                                    \
        EX_TRY_CUSTOM_UNSAFE_UNSCOPED(STATETYPE, DEFAULT_EXCEPTION_TYPE)                \


#define EX_TRY_CUSTOM_UNSAFE_UNSCOPED(STATETYPE, DEFAULT_EXCEPTION_TYPE)                \
        __fCaughtSO = false;                                                            \
        Exception*              __pExceptionPtr = NULL;                                 \
        STATETYPE               __state;                                                \
        typedef DEFAULT_EXCEPTION_TYPE  __defaultException_t;                           \
        bool                    __fCaughtCxx = false;                                   \
        bool                    __fCaughtNonCxx = false;                             \
        EnsureSOIntolerantOK(__FUNCTION__, __FILE__, __LINE__);                         \
        PAL_CPP_TRY                                                                     \
        {                                                                               \
            PAL_CPP_TRY                                                                 \
            {                                                                           \
                CAutoTryCleanup<STATETYPE> __autoCleanupTry(__state);                   \
                ANNOTATION_TRY_BEGIN;                                                   \
                /* prevent annotations from being dropped by optimizations in debug */  \
                INDEBUG(static volatile bool __alwayszero;)                             \
                INDEBUG(if(!__alwayszero))                                              \
                {                                                                       \
                    /* this is necessary for Rotor exception handling to work */        \
                    DEBUG_ASSURE_NO_RETURN_BEGIN                                        \

                                                                                        
#define EX_CATCH_IMPL                                                                   \
                    DEBUG_ASSURE_NO_RETURN_END                                          \
                }                                                                       \
                ANNOTATION_TRY_END;                                                     \
            }                                                                           \
            PAL_CPP_CATCH_EXCEPTION (__pExceptionRaw)                                   \
            {                                                                           \
                __fCaughtCxx = true;                                                    \
                __pExceptionPtr = __pExceptionRaw;                                         \
                PAL_CPP_RETHROW;                                                        \
            }                                                                           \
            PAL_CPP_ENDTRY                                                              \
        }                                                                               \
        PAL_CPP_CATCH_ALL                                                               \
        {                                                                               \
            ANNOTATION_HANDLER_BEGIN;                                                   \
            VALIDATE_BACKOUT_STACK_CONSUMPTION;                                         \
            ExceptionHolder __pException(__pExceptionPtr);                           \
            __defaultException_t __defaultException;                                    \
            if (!__fCaughtCxx)                                                          \
                __fCaughtNonCxx = true;                                                 \
            /* don't embed file names in retail to save space and avoid IP */           \
            /* a findstr /n will allow you to locate it in a pinch */                   \
            STRESS_LOG1(LF_EH, LL_INFO100, "EX_CATCH " INDEBUG(__FILE__) " line %d\n", __LINE__);\
            /* work around unreachable code warning */                                  \
            if (true) { DEBUG_ASSURE_NO_RETURN_BEGIN                                    \
            __state.SetupCatch(__fCaughtCxx);                                           \

            
#define EX_CATCH_IMPL_EX(DerivedExceptionClass)                                         \
                    DEBUG_ASSURE_NO_RETURN_END                                          \
                }                                                                       \
                ANNOTATION_TRY_END;                                                     \
            }                                                                           \
            PAL_CPP_CATCH_DERRIVED (DerivedExceptionClass, __pExceptionRaw)             \
            {                                                                           \
                __fCaughtCxx = true;                                                    \
                __pExceptionPtr = __pExceptionRaw;                                         \
                PAL_CPP_RETHROW;                                                        \
            }                                                                           \
        }                                                                               \
        PAL_CPP_CATCH_ALL                                                               \
        {                                                                               \
            ANNOTATION_HANDLER_BEGIN;                                                   \
            VALIDATE_BACKOUT_STACK_CONSUMPTION;                                         \
            __defaultException_t __defaultException;                                    \
            ExceptionHolder __pException(__pExceptionPtr);                           \
            if (!__fCaughtCxx)                                                          \
                __fCaughtNonCxx = true;                                                 \
            /* don't embed file names in retail to save space and avoid IP */           \
            /* a findstr /n will allow you to locate it in a pinch */                   \
            STRESS_LOG1(LF_EH, LL_INFO100, "EX_CATCH " INDEBUG(__FILE__) " line %d\n", __LINE__);\
            /* work around unreachable code warning */                                  \
            if (true) { DEBUG_ASSURE_NO_RETURN_BEGIN                                    \
            __state.SetupCatch(__fCaughtCxx);                                           \

//
// What we really need a different version of EX_TRY with one less try scope, but this
// gets us what we need for now...
//
#define EX_CATCH_IMPL_CPP_ONLY                                                          \
                    DEBUG_ASSURE_NO_RETURN_END                                          \
                }                                                                       \
                ANNOTATION_TRY_END;                                                     \
            }                                                                           \
            PAL_CPP_CATCH_EXCEPTION (__pExceptionRaw)                                   \
            {                                                                           \
                __fCaughtCxx = true;                                                    \
                __pExceptionPtr = __pExceptionRaw;                                         \
                PAL_CPP_RETHROW;                                                        \
            }                                                                           \
            PAL_CPP_ENDTRY                                                              \
        }                                                                               \
        PAL_CPP_CATCH_EXCEPTION_NOARG                                                   \
        {                                                                               \
            ANNOTATION_HANDLER_BEGIN;                                                   \
            VALIDATE_BACKOUT_STACK_CONSUMPTION;                                         \
            __defaultException_t __defaultException;                                    \
            ExceptionHolder __pException(__pExceptionPtr);                           \
            /* Note: we're always catching a C++ exception here! */                     \
            __fCaughtNonCxx = false;                                                    \
            /* don't embed file names in retail to save space and avoid IP */           \
            /* a findstr /n will allow you to locate it in a pinch */                   \
            STRESS_LOG1(LF_EH, LL_INFO100, "EX_CATCH " INDEBUG(__FILE__) " line %d\n", __LINE__);\
            /* work around unreachable code warning */                                  \
            if (true) { DEBUG_ASSURE_NO_RETURN_BEGIN                                    \
            __state.SetupCatch(__fCaughtCxx);                                           \


#define EX_CATCH    EX_CATCH_IMPL
#define EX_CATCH_EX EX_CATCH_IMPL_EX
#define EX_CATCH_CPP_ONLY EX_CATCH_IMPL_CPP_ONLY 

#define EX_END_CATCH_UNREACHABLE                                                        \
            DEBUG_ASSURE_NO_RETURN_END                                                  \
            }                                                                           \
            UNREACHABLE();                                                              \
        }                                                                               \
        PAL_CPP_ENDTRY                                                                  \
    }                                                                                   \


// "terminalexceptionpolicy" must be one of "RethrowTerminalExceptions", 
// "RethrowTransientExceptions", or "SwallowAllExceptions"

#define EX_END_CATCH_UNSAFE_UNSCOPED(terminalexceptionpolicy)                           \
            terminalexceptionpolicy;                                                    \
            __state.SucceedCatch(__fCaughtCxx);                                         \
            DEBUG_ASSURE_NO_RETURN_END                                                  \
            ANNOTATION_HANDLER_END;                                                   \
        } }                                                                             \
        EX_ENDTRY                                                                       \

            
#define EX_END_CATCH(terminalexceptionpolicy)                                           \
            terminalexceptionpolicy;                                                    \
            __state.SucceedCatch(__fCaughtCxx);                                         \
            DEBUG_ASSURE_NO_RETURN_END                                                  \
            ANNOTATION_HANDLER_END;                                                   \
        } }                                                                             \
        EX_ENDTRY                                                                       \
    }                                                                                   \

#define EX_END_CATCH_FOR_HOOK                                          \
            __state.SucceedCatch(__fCaughtCxx);                                         \
            DEBUG_ASSURE_NO_RETURN_END                                                  \
            ANNOTATION_HANDLER_END;                                                   \
        } }                                                                             \
        EX_ENDTRY                                                                       \
            
#define EX_ENDTRY                                                                       \
        PAL_CPP_ENDTRY                                                                  \
        if (__fCaughtNonCxx || __fCaughtCxx)                                            \
        {                                                                               \
            RESTORE_SO_TOLERANCE_STATE;                                                 \
        }


#define EX_TRY_FOR_FINALLY_CUSTOM(STATETYPE)                                            \
    {                                                                                   \
        EnsureSOIntolerantOK(__FUNCTION__, __FILE__, __LINE__);                         \
        STATETYPE __state;                                                              \
        PAL_TRY                                                                         \
        {                                                                               \
            /* this is necessary for Rotor exception handling to work */                \
            DEBUG_ASSURE_NO_RETURN_BEGIN                                                \

            
#define EX_FINALLY                                                                      \
            DEBUG_ASSURE_NO_RETURN_END                                                  \
        }                                                                               \
        PAL_FINALLY                                                                     \
        {                                                                               \
            ANNOTATION_HANDLER_BEGIN;                                                   \
            UNSAFE_BEGIN_VALIDATE_BACKOUT_STACK_CONSUMPTION_NO_DISABLE                  \
            __state.CleanupTry();                                                       \


#define EX_END_FINALLY                                                                  \
            ANNOTATION_HANDLER_END;                                                     \
            UNSAFE_END_VALIDATE_BACKOUT_STACK_CONSUMPTION_NO_DISABLE                    \
        }                                                                               \
        PAL_ENDTRY                                                                      \
    }                                                                                   \


#define EX_RETHROW                                                                      \
        {                                                                               \
            __pException.SuppressRelease();                                       \
            PAL_CPP_RETHROW;                                                            \
        }                                                                               \

            
#define GET_EXCEPTION() (__pException.IsNull() ? &__defaultException : __pException.GetValue())
#define EXTRACT_EXCEPTION() (__pException.Extract())


//==============================================================================
// High-level macros for common uses of EX_TRY. Try using these rather
// than the raw EX_TRY constructs.
//==============================================================================

//===================================================================================
// Macro for defining external entrypoints such as COM interop boundaries.
// The boundary will catch all exceptions (including terminals) and convert
// them into HR/IErrorInfo pairs as appropriate.
//
// Usage:
//
//   HRESULT hr = S_OK;
//   EX_TRY
//   <do managed stuff>
//   EX_CATCH_HRESULT(hr);
//   return hr;
//
// Comments:
//   Note that IErrorInfo will automatically be set up on the thread if appropriate.
//===================================================================================

#define EX_CATCH_HRESULT(_hr)                                                   \
    EX_CATCH                                                                    \
    {                                                                           \
        (_hr) = GET_EXCEPTION()->GetHR();                                       \
        /* Enable this assert after we fix EH so that GetHR() never */          \
        /* mistakenly returns S_OK */                                           \
        /***/                                                                   \
        /* _ASSERTE(FAILED(_hr)); */                                            \
        IErrorInfo *pErr = GET_EXCEPTION()->GetErrorInfo();                     \
        if (pErr != NULL)                                                       \
        {                                                                       \
            SetErrorInfo(0, pErr);                                              \
            pErr->Release();                                                    \
        }                                                                       \
    }                                                                           \
    EX_END_CATCH(SwallowAllExceptions)


//===================================================================================
// Variant of the above Macro for used by ngen and mscorsvc to add
// a RetailAssert when a reg key is set if we get an unexpected HRESULT
// from one of the RPC calls.
//===================================================================================
void RetailAssertIfExpectedClean();             // Defined in src\util\Debug.cpp

#define EX_CATCH_HRESULT_AND_NGEN_CLEAN(_hr)                                    \
    EX_CATCH                                                                    \
    {                                                                           \
        (_hr) = GET_EXCEPTION()->GetHR();                                       \
        RetailAssertIfExpectedClean();                                          \
        /* Enable this assert after we fix EH so that GetHR() never */          \
        /* mistakenly returns S_OK */                                           \
        /***/                                                                   \
        /* _ASSERTE(FAILED(_hr)); */                                            \
        IErrorInfo *pErr = GET_EXCEPTION()->GetErrorInfo();                     \
        if (pErr != NULL)                                                       \
        {                                                                       \
            SetErrorInfo(0, pErr);                                              \
            pErr->Release();                                                    \
        }                                                                       \
    }                                                                           \
    EX_END_CATCH(SwallowAllExceptions)


//===================================================================================
// Macro for containing normal exceptions but letting terminal exceptions continue to propagate.
//
// Usage:
//
//  EX_TRY
//  {
//      ...your stuff...
//  }
//  EX_SWALLOW_NONTERMINAL
//
// Remember, terminal exceptions (such as ThreadAbort) will still throw out of this
// block. So don't use this as a substitute for exception-safe cleanup!
//===================================================================================

#define EX_SWALLOW_NONTERMINAL                           \
    EX_CATCH                                             \
    {                                                    \
    }                                                    \
    EX_END_CATCH(RethrowTerminalExceptions)              \


//===================================================================================
// Macro for containing normal exceptions but letting transient exceptions continue to propagate.
//
// Usage:
//
//  EX_TRY
//  {
//      ...your stuff...
//  }
//  EX_SWALLOW_NONTRANSIENT
//
// Terminal exceptions (such as ThreadAbort and OutOfMemory) will still throw out of this
// block. So don't use this as a substitute for exception-safe cleanup!
//===================================================================================

#define EX_SWALLOW_NONTRANSIENT                          \
    EX_CATCH                                             \
    {                                                    \
    }                                                    \
    EX_END_CATCH(RethrowTransientExceptions)             \


//===================================================================================
// Macro for observing or wrapping exceptions in flight.
//
// Usage:
//
//   EX_TRY
//   {
//      ... your stuff ...
//   }
//   EX_HOOK
//   {
//      ... your stuff ...
//   }
//   EX_END_HOOK
//   ... control will never get here ...
//
//
// EX_HOOK is like EX_CATCH except that you can't prevent the
// exception from being rethrown. You can throw a new exception inside the hook
// (for example, if you want to wrap the exception in flight with your own).
// But if control reaches the end of the hook, the original exception gets rethrown.
//
// Avoid using EX_HOOK for conditional backout if a destructor-based holder
// will suffice. Because these macros are implemented on top of SEH, using them will
// prevent the use of holders anywhere else inside the same function. That is, instead
// of saying this:
//
//     EX_TRY          // DON'T DO THIS
//     {
//          thing = new Thing();
//          blah
//     }
//     EX_HOOK
//     {
//          delete thing; // if it failed, we don't want to keep the Thing.
//     }
//     EX_END_HOOK
//
// do this:
//
//     Holder<Thing> thing = new Thing();   //DO THIS INSTEAD
//     blah
//     // If we got here, we succeeded. So tell holder we want to keep the thing.
//     thing.SuppressRelease();
//
//	We won't rethrow the exception if it is a Stack Overflow exception. Instead, we'll throw a new
//   exception. This will allow the stack to unwind point, and so we won't be jeopardizing a
//   second stack overflow.
//===================================================================================
#define EX_HOOK                                          \
    EX_CATCH                                             \
    {                                                    \

#define EX_END_HOOK                                      \
    }                                                    \
    ANNOTATION_HANDLER_END;                              \
    if (GET_EXCEPTION()->IsType(StackOverflowException::GetType())) \
        __fCaughtSO = TRUE;                                          \
    INTERIOR_STACK_PROBE_NOTHROW_CHECK_THREAD(__fCaughtSO=TRUE;);   \
    if (!__fCaughtSO)                                                \
        EX_RETHROW;                                                  \
    END_INTERIOR_STACK_PROBE;                                        \
    EX_END_CATCH_FOR_HOOK;                                           \
    if (__fCaughtSO)                                                 \
        PAL_CPP_THROW(Exception *, Exception::GetSOException());     \
    }                                                                \

// ---------------------------------------------------------------------------
// Inline implementations. Pay no attention to that man behind the curtain.
// ---------------------------------------------------------------------------

inline Exception::HandlerState::HandlerState()
{
    STATIC_CONTRACT_NOTHROW;
    STATIC_CONTRACT_SO_TOLERANT;

#ifdef STACK_GUARDS_DEBUG
    // If we have a debug state, use its setting for SO tolerance.  The default
    // is SO-tolerant if we have no debug state.  Can't probe w/o debug state and 
    // can't enter SO-interolant mode w/o probing.
    ClrDebugState *pDebugState = GetClrDebugState();
#endif    
}

inline void Exception::HandlerState::CleanupTry()
{
    LEAF_CONTRACT;
}

inline void Exception::HandlerState::SetupCatch(bool fCaughtInternalCxxException)
{
    LEAF_CONTRACT;
}

inline void Exception::HandlerState::SucceedCatch(bool fCaughtInternalCxxException)
{
    LEAF_CONTRACT;
}

inline HRException::HRException()
  : m_hr(E_UNEXPECTED)
{
    LEAF_CONTRACT;
}

inline HRException::HRException(HRESULT hr)
  : m_hr(hr)
{
    LEAF_CONTRACT;
}

inline HRMsgException::HRMsgException()
  : HRException()
{
    LEAF_CONTRACT;
}

inline HRMsgException::HRMsgException(HRESULT hr, SString &s)
  : HRException(hr), m_msg(s)
{
    WRAPPER_CONTRACT;
}

inline COMException::COMException()
  : HRException(NO_ERROR),
  m_pErrorInfo(NULL)
{
    WRAPPER_CONTRACT;
}

inline COMException::COMException(HRESULT hr)
  : HRException(hr),
  m_pErrorInfo(NULL)
{
    LEAF_CONTRACT;
}

inline COMException::COMException(HRESULT hr, IErrorInfo *pErrorInfo)
  : HRException(hr),
  m_pErrorInfo(pErrorInfo)
{
    LEAF_CONTRACT;
}

inline SEHException::SEHException()
{
    LEAF_CONTRACT;
    memset(&m_exception, 0, sizeof(EXCEPTION_RECORD));
}

inline SEHException::SEHException(EXCEPTION_RECORD *pointers, CONTEXT *pContext)
{
    LEAF_CONTRACT;
    memcpy(&m_exception, pointers, sizeof(EXCEPTION_RECORD));
}

// The exception throwing helpers are intentionally not inlined
// Exception throwing is a rare slow codepath that should be optimized for code size

void DECLSPEC_NORETURN ThrowHR(HRESULT hr);
void DECLSPEC_NORETURN ThrowHR(HRESULT hr, SString &msg);
void DECLSPEC_NORETURN ThrowHR(HRESULT hr, UINT uText);
void DECLSPEC_NORETURN ThrowWin32(DWORD err);
void DECLSPEC_NORETURN ThrowLastError();
void DECLSPEC_NORETURN ThrowOutOfMemory();
void DECLSPEC_NORETURN ThrowStackOverflow();
void DECLSPEC_NORETURN ThrowMessage(LPCSTR message, ...);

#undef IfFailThrow
inline void IfFailThrow(HRESULT hr)
{
    WRAPPER_CONTRACT;

    if (FAILED(hr))
    {
        ThrowHR(hr);
    }
}

inline void IfFailThrow(HRESULT hr, SString &msg)
{
    WRAPPER_CONTRACT;
    
    if (FAILED(hr))
    {
        ThrowHR(hr, msg);
    }
}

#endif  // _EX_H_
